[Android][Kotlin] KotlinでAndroidアプリケーション その2 [ボタンクリック]
こんにちは。小室です。札幌にも春が来ました。
前回、Hello Worldに相当する入門的なさわりをやってみました。表示するだけではAndroidアプリケーションを記述するとは言えません。Androidアプリケーションの実装に必要な要素を1つずつ確認していきます。
OnClickListenerを実装してみる
よく利用されるであろう、ButtonをタップするとToastを表示する簡単な実装を確認してみます。前回のプロジェクトに引き続きActivity+Fragmentのシンプルなプロジェクトを利用します。Android Studioでは以下のプロジェクトを作成しておきます。
レイアウトはこんな感じのアプリにします。実にシンプル。
レイアウトはこちら
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="New Button" android:id="@+id/button" /> </LinearLayout>
ActivityやFragmentに実装させる
まあ、この実装はあまり実用的ではないかもしれません。一つの画面にボタンがひとつだけというのは少々想定しづらいですし。まあ、何はともあれActivityクラスに
Javaの場合
public class MainJavaActivity extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(this); } @Override public void onClick(View v) { Toast.makeText(this, "Taped", Toast.LENGTH_SHORT).show(); } }
ボタンがひとつだけの画面なんてそうそうないと思うので、あんまり実用的ではないですが、ActivityやFragmentクラスで内部実装を記述することはあるかと思います。OnClickListenerの実装は、MainJavaActivityクラスにされているので、button.setOnClickListener()にはthisを突っ込んであげればOKです。Kotlinの場合を確認してみます。
Kotlinの場合
public class MainActivity : ActionBarActivity(), View.OnClickListener { override fun onCreate(savedInstanceState: Bundle?) { super<ActionBarActivity>.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button: Button = findViewById(R.id.button) as Button button.setOnClickListener(this) } override fun onClick(v: View?) { Toast.makeText(this, "Tapped", Toast.LENGTH_SHORT).show() } }
ほとんど一緒のようです。
KotlinにはInterfaceクラスではなく、Traitで実現されています。そのため、継承クラスと同列にJavaのInterfaceクラスであるView.OnClickListenerを記述します。当然実装を記述する必要があるので、Javaと同じように実装をActivityクラス内に記述します。
さらに違いというところで大きいのがsuperの記述でしょうか。superクラスは通常一つですが、Traitを利用して多重継承している関係からか、superで何のクラスを指定して曖昧さを回避する必要があるようです。
定(変)数で定義させる
クラス内に変数 or 定数として定義してしまうのもよく使う手ではないでしょうか。OnClickListenerのインターフェースクラスの実装を記述した値を変数としてクラス内部に定義します。
Javaの場合
public class MainJavaActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(listener); } private View.OnClickListener listener = new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainJavaActivity.this, "Tapped", Toast.LENGTH_SHORT).show(); } }; }
Kotlinの場合
public class MainActivity : ActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button: Button = findViewById(R.id.button) as Button button.setOnClickListener(listener) } val listener = object:View.OnClickListener { override fun onClick(v: View?) { Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show() } } }
object:という宣言を入れることで、インタフェースクラスをインスタンスとして実装ごと記述できるようです。ちなみに自分はKotlinでの、MainActivity.thisの記述方法にしばらく悩んでました。これは分からない・・・。
メソッド内部でインスタンス化させる
Javaの場合
public class MainJavaActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(MainJavaActivity.this, "Tapped", Toast.LENGTH_SHORT).show(); } }); } }
Kotlinの場合
public class MainActivity : ActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button: Button = findViewById(R.id.button) as Button button.setOnClickListener(object:View.OnClickListener { override fun onClick(v: View?) { Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show() } }) } }
より簡略化
Kotlinでは、たった一つだけのメソッドを持つクラスに関しては推論が効くらしく、さらに簡略化した形で記述が可能です。
public class MainActivity : ActionBarActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val button: Button = findViewById(R.id.button) as Button button.setOnClickListener { view -> Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show() } } }
単一の抽象メソッドを持つものをSAMと呼ぶようです。 *2。SAMとはSingle Abstract Methodの略です。Kotlinではラムダ式を活用してこういった記述が可能になるようです。Javaでは冗長であった余計な部分がかなり省かれました。
決まり文句となっていた、onClick(View view)などと言った記述を一切書かなくて良いのは非常に良いですね。ラムダ式の変数名をきちんと記述してあげれば、変換前と比べ可読性を落とすことなく、シンプルに分かりやすい記述になったかと思います。
これは多用していきたい。
まとめ
Androidで避けて通れないインタフェースクラスの実装についてJavaとKotlinを比べてみました。何番煎じか分かりませんが、自分で調べながら記述することで色々と気づくことは多いですね。インタフェースクラスを変数にする時のobject:やthis@MainActivityという記述を知る良いきっかけになりました。
だんだんとJavaでAndroidアプリケーションを記述するのが辛くなってきた今日このごろです。Kotlinの方が楽だ・・・。
参照
- プログラミング言語Kotlin 解説 - 4. Kotlinのオブジェクト指向 > 4.5 オブジェクト式とオブジェクト宣言
- Qiita - Kotlinでリスナーやコールバックをスッキリと書く【関数リテラルとSAM変換】
- 自転車で通勤しましょ♪ブログ - KOTLINでANDROIDアプリを作り直したのでTIPSを少々。
脚注
- 大通り近くのハンバーガー屋「ジャクソンビル」のチーズバーガー ↩
- Qiita - SAMインターフェースとSAM変換 ↩